package io.kiki.sba.registry.server.clientInterface.push;

import io.kiki.sba.registry.common.model.TraceTimes;
import io.kiki.sba.registry.util.StringFormatter;
import io.kiki.sba.registry.util.StringUtil;
import lombok.Getter;
import lombok.Setter;
import org.springframework.util.CollectionUtils;

import java.util.Collections;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

@Setter
@Getter
public final class TriggerPushContext {
    public final String dataNode;
    private Map<String, Long> expectDatumVersion;
    private TraceTimes firstTraceTimes;
    private TraceTimes lastTraceTimes;

    public TriggerPushContext(String dataCenter, long expectDatumVersion, String dataNode, long triggerSessionTimestamp) {
        this(Collections.singletonMap(dataCenter, expectDatumVersion), dataNode, triggerSessionTimestamp, new TraceTimes());
    }

    public TriggerPushContext(String dataCenter, long expectDatumVersion, String dataNode, long triggerSessionTimestamp, TraceTimes traceTimes) {
        this(Collections.singletonMap(dataCenter, expectDatumVersion), dataNode, triggerSessionTimestamp, traceTimes);
    }

    public TriggerPushContext(Map<String, Long> expectDatumVersion, String dataNode, long triggerSessionTimestamp) {
        this(expectDatumVersion, dataNode, triggerSessionTimestamp, new TraceTimes());
    }

    public TriggerPushContext(Map<String, Long> expectDatumVersion, String dataNode, long triggerSessionTimestamp, TraceTimes traceTimes) {
        this.dataNode = dataNode;
        this.expectDatumVersion = expectDatumVersion;
        traceTimes.setTriggerSession(triggerSessionTimestamp);
        this.firstTraceTimes = traceTimes;
        this.lastTraceTimes = traceTimes;
    }

    public synchronized Set<String> dataCenters() {
        if (CollectionUtils.isEmpty(expectDatumVersion)) {
            return Collections.emptySet();
        }
        return expectDatumVersion.keySet();
    }


    public void addTraceTime(TraceTimes times) {
        if (times.beforeThan(this.firstTraceTimes)) {
            this.firstTraceTimes = times;
        }
        if (this.lastTraceTimes.beforeThan(times)) {
            this.lastTraceTimes = times;
        }
    }

    public synchronized Map<String, Long> getExpectDatumVersion() {
        return expectDatumVersion;
    }

    public synchronized void setExpectDatumVersion(Map<String, Long> expectDatumVersion) {
        this.expectDatumVersion = expectDatumVersion;
    }

    public String formatTraceTimes(long pushFinishTimestamp) {
        if (firstTraceTimes == lastTraceTimes) {
            return StringFormatter.format("lastDataTrace={}", lastTraceTimes.format(pushFinishTimestamp));
        } else {
            return StringFormatter.format("firstDataTrace={},lastDataTrace={}", firstTraceTimes.format(pushFinishTimestamp), lastTraceTimes.format(pushFinishTimestamp));
        }
    }


    /**
     * return true when this all datacenter version are smaller than changeCtx
     *
     * @param triggerPushContext existCtx
     * @return boolean
     */
    public synchronized boolean smallerThan(TriggerPushContext triggerPushContext) {
        StringUtil.checkEquals(expectDatumVersion.keySet(), triggerPushContext.dataCenters(), "pushChangeCtx.dataCenters");

        for (Entry<String, Long> entry : this.expectDatumVersion.entrySet()) {
            Long ctxVersion = triggerPushContext.expectDatumVersion.get(entry.getKey());
            if (ctxVersion == null || entry.getValue() > ctxVersion) {
                return false;
            }
        }
        return true;
    }

    public synchronized int mergeVersion(TriggerPushContext triggerPushContext) {
        StringUtil.checkEquals(expectDatumVersion.keySet(), triggerPushContext.dataCenters(), "pushChangeCtx.dataCenters");
        int merge = 0;

        for (Entry<String, Long> entry : triggerPushContext.expectDatumVersion.entrySet()) {
            Long updateVersion = expectDatumVersion.get(entry.getKey());
            if (updateVersion == null || updateVersion < entry.getValue()) {
                expectDatumVersion.put(entry.getKey(), entry.getValue());
                merge++;
            }
        }
        return merge;
    }
}
